﻿#region

using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using Vin.Tool.Attributes;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.JsonCategory;
using Vin.Tool.Enums;

#endregion

namespace Vin.Tool.Core.BasicTypeCategory;

public static partial class Extension
{
    #region 转字典

    /// <summary>
    /// 类型转换为字典
    /// </summary>
    public static Dictionary<string, object> ToDictionary(this object obj,
        ConvertOptionType optionType = ConvertOptionType.None, bool ignoreNull = true)
    {
        var dict = new Dictionary<string, object>();
        foreach (var property in obj.GetType().GetProperties())
        {
            var value = property.GetValue(obj)?.ToString() ?? "";
            if (ignoreNull && value.IsNullOrEmpty()) continue;
            dict.Add(property.Name.ToCovert(optionType), value);
        }

        return dict.OrderBy(m => m.Key).ToDictionary(m => m.Key, m => m.Value);
    }

    public static Dictionary<string, object> ToDictionary(this JsonNode? jsonNode,
        ConvertOptionType optionType = ConvertOptionType.None, bool ignoreNull = true)
    {
        if (jsonNode == null) return new Dictionary<string, object>();
        if (jsonNode is not JsonObject jsonObj) return new Dictionary<string, object>();
        var dict = new Dictionary<string, object>();
        foreach (var property in jsonObj)
        {
            var value = property.Value?.ToString() ?? "";
            if (ignoreNull && value.IsNullOrEmpty()) continue;
            dict.Add(property.Key.ToCovert(optionType), value);
        }

        return dict.OrderBy(m => m.Key).ToDictionary(m => m.Key, m => m.Value);
    }

    public static string DictionaryToString(this Dictionary<string, object> dict, List<string>? appendList = null)
    {
        var array = dict.Select(o => $"{o.Key}={o.Value}").ToList();
        if (appendList.IsNotNull() && appendList!.Any())
        {
            array.AddRange(appendList!);
        }

        return string.Join("&", array);
    }

    public static string DictionaryToString(this Dictionary<string, object> dict, string appendStr) =>
        dict.DictionaryToString(new List<string> { appendStr });

    public static string ToQueryString(this object obj, List<string>? appendList = null,
        ConvertOptionType optionType = ConvertOptionType.None, bool ignoreNull = true)
    {
        return obj.ToDictionary(optionType, ignoreNull).DictionaryToString(appendList);
    }

    #endregion

    #region 字典转数据

    public static T? ToObject<T>(this Dictionary<string, object> dict) where T : class, new()
    {
        var referenceTypeProperties = typeof(T).GetProperties()
            .Select(p => new
            {
                Property = p,
                IsReferenceType = p.GetCustomAttribute<IsReferenceTypeAttribute>()
            })
            .Where(p => p.IsReferenceType != null)
            .ToList();
        if (!referenceTypeProperties.Any()) return dict.ToJson().ToObject<T>();

        // 先把引用类型从字典中移除，生成新的字典，然后调用.ToJson().ToObject<T>()，最后再把引用类型赋值回去
        var tempDict = dict.Where(d =>
                referenceTypeProperties.All(r => (r.IsReferenceType!.FieldName ?? r.Property.Name) != d.Key))
            .ToDictionary(d => d.Key, d => d.Value);
        var obj = tempDict.Count > 0 ? tempDict.ToJson().ToObject<T>() : new T();

        foreach (var property in referenceTypeProperties)
        {
            var key = property.IsReferenceType!.FieldName ?? property.Property.Name;
            if (dict.ContainsKey(key))
            {
                var tempObj = dict[key];
                if (tempObj is JsonNode jsonNode)
                {
                    tempObj = jsonNode.ToObject(property.Property.PropertyType);
                }

                if (tempObj is JsonElement jsonElement)
                {
                    tempObj = jsonElement.ToObject(property.Property.PropertyType);
                }

                property.Property.SetValue(obj, tempObj);
            }
        }

        // // 获取对象里面的所有为null，包含IsIsReferenceType特性的属性
        // var nullProperties = obj?.GetType().GetProperties()
        //     .Where(p => p.GetValue(obj) == null && p.GetCustomAttributes(typeof(IsReferenceTypeAttribute), true).Any())
        //     .ToList();
        // if (nullProperties != null && nullProperties.Any())
        // {
        //     foreach (var property in nullProperties)
        //     {
        //         var attribute =
        //             (IsReferenceTypeAttribute)property.GetCustomAttributes(typeof(IsReferenceTypeAttribute), true)
        //                 .First();
        //         var key = attribute.FieldName ?? property.Name;
        //         if (dict.ContainsKey(key))
        //         {
        //             property.SetValue(obj, dict[key]);
        //         }
        //     }
        // }

        return obj;
    }

    #endregion

    #region 获取属性值

    /// <summary>
    /// 获取属性值
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public static object? GetPropertyValue(this object? obj, string propertyName)
    {
        if (obj == null) return null;
        var property = obj.GetType().GetProperty(propertyName);
        return property?.GetValue(obj);
    }

    /// <summary>
    /// 根据路径获取属性值
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="propertyPath"></param>
    /// <param name="separator"></param>
    /// <returns></returns>
    public static object? GetNestedPropertyValue(this object? obj, string propertyPath, string separator = ".")
    {
        if (obj == null) return null;
        foreach (var propertyName in propertyPath.Split(separator))
        {
            var property = obj.GetType().GetProperty(propertyName);
            if (property == null) return null;

            obj = property.GetValue(obj);
            if (obj == null) return null;
        }

        return obj;
    }

    // 根据路径获取数组的值
    public static object? GetNestedArrayValue(this object? obj, string propertyPath, string separator = ".")
    {
        if (obj == null) return null;
        foreach (var propertyName in propertyPath.Split(separator))
        {
            if (propertyName.Contains("["))
            {
                var index = int.Parse(Regex.Match(propertyName, @"\d+").Value);
                var arrayName = propertyName.Substring(0, propertyName.IndexOf("[", StringComparison.Ordinal));
                var property = obj.GetType().GetProperty(arrayName);
                if (property == null) return null;

                obj = property.GetValue(obj);
                if (obj == null) return null;

                obj = ((object[])obj)[index];
            }
            else
            {
                var property = obj.GetType().GetProperty(propertyName);
                if (property == null) return null;

                obj = property.GetValue(obj);
                if (obj == null) return null;
            }
        }

        return obj;
    }

    #endregion

    #region 克隆

    public static T? Clone<T>(this T? obj) where T : class
    {
        return obj?.ToJson().ToObject<T>();
    }

    #endregion
}